package com.cnwidsom.transfomer;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cnwidsom.config.MonitorConfig;
import com.cnwidsom.plugins.PluginUtils;
import com.cnwidsom.plugins.type.TypeInjector;
import com.cnwidsom.utils.PathUtils;

import javassist.ByteArrayClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.LoaderClassPath;
import javassist.Modifier;
import javassist.NotFoundException;

public class MonitorClassFileTransformer implements ClassFileTransformer {
	private static final Logger log = LoggerFactory.getLogger(MonitorClassFileTransformer.class);
	private static final String SUFFIX = "$MonitorImpl";
	private String path = null;
	private MonitorConfig mc = null;

	public MonitorClassFileTransformer(String path) {
		this.path = path;
	}

	private CtClass getClass(ClassLoader loader, String nClassName, byte[] classfileBuffer, MonitorConfig.MonitorItemConfig mic) throws NotFoundException {
		// ClassPool.getDefault().importPackage("com.cnwidsom.monitor.*");
		// 用于取得字节码类，必须在当前的classpath中，使用全称
		ClassPool pool = ClassPool.getDefault();
		pool.appendClassPath(new LoaderClassPath(loader));
		CtClass ctClass = null;
		try {
			ctClass = pool.get(nClassName);
		} catch (NotFoundException ex) {
			boolean isMonitorProxyClass = mc.isDefaultMonitorProxyClass();
			if (mic != null) {
				if (mic.isMonitorProxyClass() != null)
					isMonitorProxyClass = mic.isMonitorProxyClass();
			} else {
				isMonitorProxyClass = true;
			}
			if (isMonitorProxyClass) {
				System.err.println("采用字节码生成方式:" + nClassName);
				log.info("采用字节码生成方式" + nClassName);
				pool.appendClassPath(new ByteArrayClassPath(nClassName, classfileBuffer));
				ctClass = pool.get(nClassName);
				if (ctClass == null) {
					log.error(nClassName + "动态代理失败");
					return null;
				}
			} else {
				log.warn("无法找到该类定义,可能是由代理自动生成:" + nClassName);
				System.err.println("无法找到该类定义,可能是由代理自动生成:" + nClassName);
				return null;
			}
		}
		ctClass.defrost();
		return ctClass;
	}

	private String getNewMethodName(CtClass ctClass, String methodName) {
		int nowNum = 0;
		String prefix_method_name = methodName + SUFFIX;
		String suffix_method_name = "";
		if (methodName.contains(SUFFIX)) {
			int pos = methodName.indexOf(SUFFIX);
			prefix_method_name = methodName.substring(0, pos + SUFFIX.length());
			String numStr = "";
			for (int i = pos + SUFFIX.length() + 1; i < methodName.length() && methodName.charAt(i) >= 0x30 && methodName.charAt(i) <= 0x39; i++) {
				numStr += methodName.charAt(i);
				pos++;
			}
			if (pos < methodName.length()) {
				suffix_method_name = methodName.substring(pos);
			}
			if (!numStr.trim().equalsIgnoreCase("")) {
				try {
					nowNum = Integer.parseInt(numStr);
					nowNum++;
				} catch (Exception ex) {
					int a = 0;
				}
			}

		}

		try {
			while (ctClass.getDeclaredMethod(prefix_method_name + nowNum + suffix_method_name) != null) {
				nowNum++;
			}
		} catch (NotFoundException e) {

		}
		return prefix_method_name + nowNum + suffix_method_name;
	}

	public byte[] transform(ClassLoader cloader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer)
			throws IllegalClassFormatException {
		StringBuffer codeAppender = new StringBuffer();
		try {
			// System.out.println("Loading " + className + "...");
			ClassLoader loader = Thread.currentThread().getContextClassLoader();

			if (mc == null) {
				if (path != null && !path.trim().equalsIgnoreCase("")) {
					try {
						// classLoader为空时可能是正在加载jdk提供的类,此时无法获得classpath,因此如果想对jdk提供的类进行监控,需要提供配置文件的绝对路径
						if (loader == null && path.startsWith("classpath:"))
							return classfileBuffer;
						else
							mc = MonitorConfig.parseConfig(loader, path);
					} catch (Exception e) {
						e.printStackTrace();
						mc = null;
						return null;
					}
				} else {
					mc = MonitorConfig.initDefault();
				}
			}

			CtClass ctClass = null;
			String nClassName = className.replace("/", ".");

			boolean isIncludePath = false;
			if (mc.getIncludes().size() == 0) {
				isIncludePath = true;
			}

			MonitorConfig.MonitorItemConfig cur_mic = null;
			for (MonitorConfig.MonitorItemConfig mic : mc.getIncludes()) {
				if (MonitorConfig.buildClassPackagePattern(mic.getPath()).matcher(nClassName).matches()) {
					isIncludePath = true;
					cur_mic = mic;
					break;
				}
			}

			for (int i = 0; i < mc.getMonitorTypes().size(); i++) {
				TypeInjector ti = PluginUtils.getTypeInjectorByName(mc.getMonitorTypes().get(i));
				if (ti != null && ti.canInject(nClassName)) {
					ctClass = getClass(loader, nClassName, classfileBuffer, cur_mic);
					byte[] classCode = ti.inject(loader, ctClass);
					if (classCode != null) {
						ctClass.defrost();
						return classCode;
					}
				}
			}

			for (String exlude : mc.getExcludes()) {
				if (MonitorConfig.buildClassPackagePattern(exlude).matcher(nClassName).matches()) {
					isIncludePath = false;
					break;
				}
			}
			if (!isIncludePath) {
				return classfileBuffer;
			}

			if (ctClass == null)
				ctClass = getClass(loader, nClassName, classfileBuffer, cur_mic);
			if (ctClass == null)
				return classfileBuffer;
			log.info("Injecting " + className + " ...");
			// System.out.println("Injecting " + className + " ...");

			if (ctClass.isInterface()) {
				log.warn("接口类不能监控:" + ctClass.getName());
				return classfileBuffer;
			}

			for (CtMethod mold : ctClass.getDeclaredMethods()) {
				if (Modifier.isAbstract(mold.getModifiers())) {
					log.warn("抽象方法不能监控:" + mold.getName());
					continue;
				}

				String old_name = mold.getName();
				String classMethodName = PathUtils.getMethodClassName(mold.getLongName());
				String new_name = getNewMethodName(ctClass, old_name);

				if (cur_mic == null || !cur_mic.getPath_pattern().matcher(classMethodName).matches()) {
					continue;
				}
				System.out.println("Injecting " + mold.getLongName() + " ...");

				CtClass returnClass = mold.getReturnType();
				if (returnClass.getName().equalsIgnoreCase("void")) {
					returnClass = null;
				}

				// System.out.println("Injecting Method " + old_name + " ...");

				// 修改原有的方法名称
				// mold.setName(new_name);

				String matchName = null;

				for (String pathName : mc.getStartPaths().keySet()) {
					for (String path : mc.getStartPaths().get(pathName).keySet()) {
						Pattern p = mc.getStartPaths().get(pathName).get(path);
						if (p.matcher(classMethodName).matches()) {
							matchName = pathName;
							break;
						}
					}
				}

				CtMethod mnew = null;

				codeAppender = new StringBuffer();
				codeAppender.append("{\n");

				if (matchName != null) {
					codeAppender.append("com.cnwidsom.monitor.Token.newToken(\"" + matchName + "\");\n");
				} else {
					codeAppender.append("com.cnwidsom.monitor.Token.createNextToken(\"" + mold.getName() + "\");\n");
				}

				if (cur_mic.getType().equals(MonitorConfig.DATA_TYPE_REMOTE_ENTITY)) {// 如果是RPC参数则需要增加REMOTE_CALL_ID而不记录调用时间

				} else {// 替换新方法
					// 创建新的方法，复制原来的方法
					mnew = CtNewMethod.copy(mold, new_name, ctClass, null);

					codeAppender.append("java.util.List ex_list = new java.util.ArrayList();\n");
					codeAppender.append("long start = System.currentTimeMillis();\n");
					codeAppender.append("try{\n");

					if (returnClass == null)
						codeAppender.append(new_name + "($$);\n");
					else
						codeAppender.append(returnClass.getName() + " r= " + new_name + "($$);\n");

					if (returnClass != null)
						codeAppender.append("return r;\n");
					CtClass[] exceptionClasses = mold.getExceptionTypes();
					if (exceptionClasses != null && exceptionClasses.length > 0) {
						for (int i = 0; i < exceptionClasses.length; i++) {
							CtClass exceptionClass = exceptionClasses[i];
							codeAppender.append("}catch(" + exceptionClass.getName() + " ex" + i + "){\n");
							codeAppender.append("ex_list.add(ex" + i + ");\n");
							codeAppender.append("throw ex" + i + ";\n");
						}
					} else {
						codeAppender.append("}catch(RuntimeException ex){\n");
						codeAppender.append("ex_list.add(ex);\n");
						codeAppender.append("throw ex;\n");
					}
					codeAppender.append("}finally{\n");

					codeAppender.append("long runTime = System.currentTimeMillis()-start;\n");
					codeAppender
							.append("com.cnwidsom.monitor.MonitorItem mi = new com.cnwidsom.monitor.MonitorItem(com.cnwidsom.monitor.Token.getTokenHeader(), \""
									+ mold.getLongName() + "\", runTime, ex_list, System.currentTimeMillis(), null);\n");
					codeAppender.append("java.util.Map params=new java.util.HashMap();\n");
					// 记录调用参数
					int length = mold.getParameterTypes() != null ? mold.getParameterTypes().length : 0;
					for (int i = 1; i < length + 1; i++) {
						if (mold.getParameterTypes()[i - 1].isPrimitive()) {
							codeAppender.append("params.put(\"" + mold.getParameterTypes()[i - 1].getSimpleName() + "\",String.valueOf($" + i + "));\n");
						} else {
							codeAppender.append("params.put(\"" + mold.getParameterTypes()[i - 1].getSimpleName() + "\",$" + i + ");\n");
						}
					}
					codeAppender.append("mi.setParameters(params);\n");
					codeAppender.append("com.cnwidsom.monitor.Token.endBackToken(mi);\n");
					codeAppender.append("java.util.List token_list = com.cnwidsom.monitor.Token.releaseToken();\n");
					codeAppender.append("if(token_list!=null){\n");
					codeAppender.append("com.cnwidsom.monitor.MonitorLogWritter.log(token_list);\n");
					codeAppender.append("}\n");
					codeAppender.append("}\n");
				}
				// codeAppender.append("System.out.println(\"[" +
				// mold.getLongName() + "]RunTime:\"+runTime);\n");

				codeAppender.append("}");

				// 增加新方法
				ctClass.addMethod(mnew);

				mold.setBody(codeAppender.toString());

			}

			byte[] b = ctClass.toBytecode();
			ctClass.defrost();
			return b;
		} catch (Exception ex) {
			ex.printStackTrace();
			System.err.println(codeAppender);
		}
		return classfileBuffer;
	}
}
